package com.shuiyun.jtt.utils;

import cn.hutool.core.codec.BCD;
import cn.hutool.core.util.StrUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author shuiyun
 * @program jtt1078
 * @description Byte读取工具类
 * @date 2021-09-04 11:03
 **/
public class ByteUtil {

    /**
     * 返回一个bcd 高四位为一个数字 第四位为一个数字
     *
     * @param b 字节
     * @return bcd字符串
     */
    public static String getBcd(byte b) {
        int ch1 = (b >> 4) & 0x0f;
        int ch2 = (b & 0x0f);
        return ch1 + "" + ch2;
    }

    /**
     * @param data 待转换数据
     * @return 字节数组
     * @author shuiyun
     * @date 2021/9/7
     */
    public static byte[] getThreeBytes(Integer data) {
        byte[] bytes = new byte[3];
        bytes[0] = (byte) ((data >> 16) & 0xff);
        bytes[1] = (byte) ((data >> 8) & 0xff);
        bytes[2] = (byte) (data & 0xff);
        return bytes;
    }

    /**
     * 数组转换成十六进制字符串
     *
     * @param array 字节数组
     * @return HexString
     */
    public static String bytes2HexStr(byte[] array) {
        if (null == array || array.length < 1) {
            return "";
        }
        StringBuilder sb = new StringBuilder(array.length);
        String sTemp;
        for (byte b : array) {
            sTemp = Integer.toHexString(0xff & b);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将ByteBuf转换成16进制字符串
     *
     * @param buf 待转换的ByteBuf
     * @return 十六进制字符串
     * @author shuiyun
     * @date 2022/4/18
     */
    public static String bytes2HexStr(ByteBuf buf) {
        byte[] bytes = readReadableBytes(buf);
        return bytes2HexStr(bytes);
    }

    /**
     * 数组转换成带0x的十六进制字符串
     *
     * @param array
     * @return HexString
     */
    public static String bytes2FullHexStr(byte[] array) {
        StringBuilder sb = new StringBuilder(array.length);
        sb.append("0x");
        String sTemp;
        for (int i = 0; i < array.length; i++) {
            sTemp = Integer.toHexString(0xFF & array[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp);
            if (i < array.length - 1) {
                sb.append("0x");
            }
        }
        return sb.toString().toLowerCase();
    }


    /**
     * 把16进制字符串转换成字节数组
     *
     * @param hex 16进制字符串
     * @return byte[]
     */
    public static byte[] hexStr2Bytes(String hex) {
        if (StrUtil.isBlank(hex)) {
            return new byte[0];
        }
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] chars = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(chars[pos]) << 4 | toByte(chars[pos + 1]));
        }
        return result;
    }

    /**
     * 相当于(short *) byte_pointer的效果
     *
     * @param src 输入的字节数组
     * @return 字节数组
     * @author shuiyun
     * @date 2021-09-07 15:53
     */
    public static short[] toShortArray(byte[] src) {
        short[] dst = new short[src.length / 2];
        for (int i = 0, k = 0; i < src.length; ) {
            dst[k++] = (short) ((src[i++] & 0xff) | ((src[i++] & 0xff) << 8));
        }
        return dst;
    }

    /**
     * 读取byteBuf剩下的字节
     *
     * @param msg 可读取byteBuf
     * @return 剩下所有的字节
     * @author shuiyun
     * @date 2021-09-07 16:15
     */
    public static byte[] readReadableBytes(ByteBuf msg) {
        byte[] content = new byte[msg.readableBytes()];
        msg.readBytes(content);
        msg.release();
        return content;
    }

    private static int toByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    /**
     * 根据字节码生成字节流
     *
     * @param bytes 输入的字节码数组
     * @return byteBuf
     * @author shuiyun
     * @date 2022-04-07 10:27
     */
    public static ByteBuf getBuf(byte[] bytes) {
        ByteBuf buf = Unpooled.buffer();
        buf.writeBytes(bytes);
        return buf;
    }

    /**
     * 校验
     */
    public static boolean verify(ByteBuf buf) {
        byte checkCode = buf.getByte(buf.readableBytes() - 1);
        buf = buf.slice(0, buf.readableBytes() - 1);
        byte calculatedCheckCode = bcc(buf);

        return checkCode == calculatedCheckCode;
    }

    /**
     * BCC校验(异或校验)
     */
    public static byte bcc(ByteBuf byteBuf) {
        byte cs = 0;
        while (byteBuf.isReadable()) {
            cs ^= byteBuf.readByte();
        }
        byteBuf.resetReaderIndex();
        return cs;
    }

    /**
     * BCC校验(异或校验)
     */
    public static byte bcc(byte[] bytes) {
        byte cs = 0;
        for (byte aByte : bytes) {
            cs ^= aByte;
        }
        return cs;
    }


    /**
     * 数据拆分
     *
     * @param source 原数据
     * @return
     * @author shuiyun
     * @date 2022-04-20 16:54
     */
    public static List<ByteBuf> slice(ByteBuf source, int size) {
        int total = source.writerIndex() / size;
        List<ByteBuf> result = new ArrayList<>();
        int start = 0;
        for (int i = 0; i < total; i++) {
            result.add(source.slice(start, size));
            start += size;
        }
        if (source.writerIndex() > start) {
            result.add(source.slice(start, source.writerIndex() - start));
        }
        return result;
    }

    /**
     * 数据源转义
     * 1、 0x7e -> 0x7d0x01
     * 2、 0x7e -> 0x7d0x02
     *
     * @param buf     字节数组
     * @param oldByte    原字节
     * @param newByteBuf 新字节流
     * @return 转义好的数据
     * @author shuiyun
     * @date 2022/4/23 19:52
     */
    public static ByteBuf escape(ByteBuf buf, byte oldByte, ByteBuf newByteBuf) {
        List<ByteBuf> list = new ArrayList<>();
        int index = buf.bytesBefore(oldByte);
        while (index > -1) {
            if (index > 0) {
                list.add(buf.readSlice(index));
            }
            buf.skipBytes(1);
            list.add(newByteBuf);
            index = buf.bytesBefore(oldByte);
        }
        list.add(buf.readSlice(buf.readableBytes()));
        return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, list.size(), list);
    }

    /**
     * 数据源反转义
     *
     * @param source     源数据
     * @param oldByte    原字节数组 多字节转单字节
     * @param newByteBuf 新输入的数据
     * @return 反转义好的数据
     * @author shuiyun
     * @date 2022/4/23 19:53
     */
    public static ByteBuf unEscape(ByteBuf buf, byte[] oldByte, ByteBuf newByteBuf) {
        ByteBuf tempBuf = Unpooled.wrappedBuffer(new byte[]{oldByte[0]});
        List<ByteBuf> list = new ArrayList<>();
        int index = buf.bytesBefore(oldByte[0]);
        while (index > -1) {
            if (index > 0) {
                list.add(buf.readSlice(index));
            }

            if (buf.readableBytes() >= oldByte.length) {
                byte[] bytes = new byte[oldByte.length];
                buf.readBytes(bytes);
                if (bytesComp(bytes, oldByte)) {
                    list.add(newByteBuf);
                } else {
                    buf.readerIndex(buf.readerIndex() - bytes.length);
                    buf.skipBytes(1);
                    list.add(tempBuf);
                }
            } else {
                buf.skipBytes(1);
                list.add(tempBuf);
            }
            index = buf.bytesBefore(oldByte[0]);
        }
        list.add(buf.readSlice(buf.readableBytes()));
        return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, list.size(), list);
    }

    /**
     * 字节数组对比
     *
     * @param source 第一个数组
     * @param now    第二个字节数组
     * @return 对比是否一样
     * @author shuiyun
     * @date 2022/4/26 21:33
     */
    private static boolean bytesComp(byte[] source, byte[] now) {
        if (source.length != now.length) {
            return false;
        }
        String sourceStr = BCD.bcdToStr(source);
        String nowStr = BCD.bcdToStr(now);
        return sourceStr.equals(nowStr);
    }

    /**
     * 跳过指定长度 读取指定长度的字节数组
     * @param bytes 被读取的数据
     * @param skip 跳过的字节数
     * @param length 读取的长度
     * @author shuiyun
     * @date 2022/5/15 16:15
     * @return 读取好的字节数
     */
    public static byte[] skipRead(byte[] bytes, int skip, int length) {
        if (bytes.length < (skip + length)) {
            throw new RuntimeException("Need " + (skip + length) + " bytes, input " + bytes.length + " bytes");
        }
        byte[] result = new byte[length];
        System.arraycopy(bytes, skip, result, 0, length);
        return result;
    }

    /**
     * 字符串数组累加 可以同时接收多个数组
     * @param bytes 字节数组集合或二维字节数组
     * @author shuiyun
     * @date 2022/5/15 16:32
     * @return 累加的字节数组
     */
    public static byte[] addBytes(byte[]... bytes) {
        int sum = Arrays.stream(bytes).mapToInt(p -> p.length).sum();
        byte[] result = new byte[sum];

        int start = 0;
        for (byte[] b : bytes) {
            System.arraycopy(b, 0, result, start, b.length);
            start += b.length;
        }
        return result;
    }

    /**
     * 指定或使用默认值初始化byte数组
     * @param length 长度
     * @author shuiyun
     * @date 2022/5/15 17:04
     * @return 指定长度的字节数据 可以指定填充的字节
     */
    public static byte[] initBytes(int length,byte... pads){
        byte pad = 0x00;
        if (pads.length > 0){
            pad = pads[0];
        }
        if (length < 1){
            return new byte[0];
        }
        byte[] bytes = new byte[length];
        for (int i = 0; i < length; i++) {
            bytes[i] = pad;
        }
        return bytes;
    }
}
